
#include <iostream>
#include <cstring>
#include <cstdio>
#include <map>
#include <vector>
#include <string>
#include <ctime>
#include <cmath>
#include <cstdlib>
#include <sstream>


/***for python ****/

#include <Python.h>

using namespace std;

#define pi 3.1415926535897932384626433832795

bool L1_flag = 1;
bool initializeFlag;

FILE *f2;
FILE *f3;

FILE *entityFile;
FILE *relationFile;

PyObject *pModule;

//normal distribution
double rand(double min, double max)
{
    return min + (max - min) * rand() / (RAND_MAX + 1.0);
}
double normal(double x, double miu, double sigma)
{
    return 1.0 / sqrt(2 * pi) / sigma * exp(-1 * (x - miu) * (x - miu) / (2 * sigma * sigma));
}
double randn(double miu, double sigma, double min, double max)
{
    double x, y, dScope;
    do
    {
        x = rand(min, max);
        y = normal(x, miu, sigma);
        dScope = rand(0.0, normal(miu, miu, sigma));
    } while (dScope > y);
    return x;
}

double sqr(double x)
{
    return x * x;
}

double vec_len(vector<double> &a)
{
    double res = 0;
    for (int i = 0; i < a.size(); i++)
        res += a[i] * a[i];
    res = sqrt(res);
    return res;
}

string version;
char buf[100000], buf1[100000];
int relation_num, entity_num;
map<string, int> relation2id, entity2id;
map<int, string> id2entity, id2relation;

map<int, map<int, int> > left_entity, right_entity;
map<int, double> left_num, right_num;

class Train
{

public:
    map<pair<int, int>, map<int, int> > ok;
    void add(int x, int y, int z)
    {
        fb_h.push_back(x);
        fb_r.push_back(z);
        fb_l.push_back(y);
        ok[make_pair(x, z)][y] = 1;
    }

    void initialize(int n_in, double rate_in, double margin_in, int method_in){
        n = n_in;
        rate = rate_in;
        margin = margin_in;
        method = method_in;

        /* set the relation_num * dim  relation matrix*/
        relation_vec.resize(relation_num);
        for (int i = 0; i < relation_vec.size(); i++)
            relation_vec[i].resize(n);
        
        /* set the entity_num * dim enetiy*/
        entity_vec.resize(entity_num);
        for (int i = 0; i < entity_vec.size(); i++)
            entity_vec[i].resize(n);

        
        relation_tmp.resize(relation_num);
        for (int i = 0; i < relation_tmp.size(); i++)
            relation_tmp[i].resize(n);
        
        entity_tmp.resize(entity_num);
        for (int i = 0; i < entity_tmp.size(); i++)
            entity_tmp[i].resize(n);
        
        for (int i = 0; i < relation_num; i++)
        {
            for (int ii = 0; ii < n; ii++)
                relation_vec[i][ii] = randn(0, 1.0 / n, -6 / sqrt(n), 6 / sqrt(n));
        }
        
        for (int i = 0; i < entity_num; i++)
        {
            for (int ii = 0; ii < n; ii++)
                entity_vec[i][ii] = randn(0, 1.0 / n, -6 / sqrt(n), 6 / sqrt(n));
            norm(entity_vec[i]);
        }
    }



    void run(int n_in, double rate_in, double margin_in, int method_in)
    {
        if(initializeFlag != 0){  
            //update the latest embedding values
            //call the function to get the id and embedding of the train     
            PyObject *pLatestEntFunc = PyObject_GetAttrString(pModule, "getLatestEnt");
            PyObject *pLatestEntListRet = PyObject_CallObject(pLatestEntFunc, NULL);
            PyObject *latestEntListItem;    //all entity ids and embedded arrays
            PyObject *latestEntItem;    //entity ids and embedding arrays for each row tuple

            for (int i = 0; i < entity_num; i++)
            {
                latestEntListItem = PyList_GetItem(pLatestEntListRet, i);   
                //Get the i-th row of the list
                latestEntItem = PyList_GetItem(latestEntListItem, 0);
                //Get the 0th column of the i-th row
                int tmpId;
                PyArg_Parse(latestEntItem, "i", &tmpId);
                latestEntItem = PyList_GetItem(latestEntListItem, 1);       //Get the first column of row i, i.e. the array
                for(int ii = 0; ii < n; ii++){
                    PyObject *tmpItem = PyList_GetItem(latestEntItem, ii);
                    float tmp;
                    PyArg_Parse(tmpItem, "f", &tmp);
                    entity_vec[tmpId][ii] = tmp;
                }
            }


            PyObject *pLatestRelFunc = PyObject_GetAttrString(pModule, "getLatestRel");
            PyObject *pLatestRelListRet = PyObject_CallObject(pLatestRelFunc, NULL);
            PyObject *latestRelListItem;    //All entity ids and embedded arrays
            PyObject *latestRelItem;    //Entity ids and embedding arrays for each row tuple

            for (int i = 0; i < relation_num; i++)
            {
                latestRelListItem = PyList_GetItem(pLatestRelListRet, i);   
                //Get the i-th row of the list
                latestRelItem = PyList_GetItem(latestRelListItem, 0);
                //Get the 0th column of the i-th row
                int tmpId;
                PyArg_Parse(latestRelItem, "i", &tmpId);
                latestRelItem = PyList_GetItem(latestRelListItem, 1);
                for(int ii = 0; ii < n; ii++){
                    PyObject *tmpItem = PyList_GetItem(latestRelItem, ii);
                    float tmp;
                    PyArg_Parse(tmpItem, "f", &tmp);
                    relation_vec[tmpId][ii] = tmp;
                }
            }
        }

        else{
            initializeFlag = 1;
            //The embedding values are only initialized in the initialize function
        }
        
        bfgs();
        /* Broyden–Fletcher–Goldfarb–Shanno（BFGS）*/
    }

private:
    int n, method;
    double res;           //loss function value
    double count, count1; //loss function gradient
    double rate, margin;
    double belta;
    vector<int> fb_h, fb_l, fb_r;
    vector<vector<int> > feature; 
    vector<vector<double> > relation_vec, entity_vec;
    vector<vector<double> > relation_tmp, entity_tmp;
    double norm(vector<double> &a)
    {
        double x = vec_len(a);
        if (x > 1)
            for (int ii = 0; ii < a.size(); ii++)
                a[ii] /= x;
        return 0;
    }

    int rand_max(int x)
    {
        int res = (rand() * rand()) % x;
        while (res < 0)
            res += x;
        return res;
    }

    void txtToDatabase()
    {
        //After the epoch theory iteration, update back to the database
        cout<<"finish epoch training, begin to write database"<<endl;

        // copy entity to database
        PyObject *pFuncWriteEntityDatabase = PyObject_GetAttrString(pModule, "writeEntityToDatabase"); /*更新关系嵌入值*/
        if (!pFuncWriteEntityDatabase || !PyCallable_Check(pFuncWriteEntityDatabase))
        {
            printf("not found function writeEntityToDatabase");
            return;
        }

        PyObject *pRetEntity = PyObject_CallObject(pFuncWriteEntityDatabase, NULL);

        //copy relaiton to database
        PyObject *pFuncWriteRelationDatabase = PyObject_GetAttrString(pModule, "writeRelationToDatabase"); /*更新关系嵌入值*/
        if (!pFuncWriteRelationDatabase || !PyCallable_Check(pFuncWriteRelationDatabase))
        {
            printf("not found function writeRelationToDatabase");
            return;
        }

        PyObject *pRetRelation = PyObject_CallObject(pFuncWriteRelationDatabase, NULL);


    }

    void writeDataBaseByStep()  //Single step-by-step database update, for test
    {
        for (int i = 0; i < relation_num; i++)
            {
                std::stringstream embeddingVal;    //Update embedded values back to the database
                embeddingVal.precision(6);
                embeddingVal.setf(std::ios::fixed);
                for (int ii = 0; ii < n; ii++){     
                    
                    // fprintf(f2, "%.6lf\t", relation_vec[i][ii]);
                    if(ii == n - 1){
                        embeddingVal << relation_vec[i][ii];
                    }
                    else{
                        embeddingVal << relation_vec[i][ii] <<",";
                    }
                    
                }

                string tmpEmbStr;
                tmpEmbStr = embeddingVal.str();
                PyObject *pFuncUpdateRelation = PyObject_GetAttrString(pModule, "updateRelationEmbedding"); /*更新关系嵌入值*/
                if (!pFuncUpdateRelation || !PyCallable_Check(pFuncUpdateRelation))
                {
                    printf("not found function updateRelationEmbedding");
                    return;
                }
                PyObject *pArgs = PyTuple_New(2);   
                PyTuple_SetItem(pArgs, 0, Py_BuildValue("s", tmpEmbStr.c_str())); 
                PyTuple_SetItem(pArgs, 1, Py_BuildValue("i", i));

                PyObject *pRet = PyObject_CallObject(pFuncUpdateRelation, pArgs);
            }
            
            /* print entity_vec to entity2vec.bern file*/
            for (int i = 0; i < entity_num; i++)
            {
                std::stringstream embeddingVal;
                embeddingVal.precision(6);
                embeddingVal.setf(std::ios::fixed);

                for (int ii = 0; ii < n; ii++){
                    // fprintf(f3, "%.6lf\t", entity_vec[i][ii]);
                    if(ii == n - 1){
                        embeddingVal << entity_vec[i][ii];
                    }
                    else{
                        embeddingVal << entity_vec[i][ii] <<",";
                    }
                }

                string tmpEmbStr;
                tmpEmbStr = embeddingVal.str();
                PyObject *pFuncUpdateEntity = PyObject_GetAttrString(pModule, "updateEntityEmbedding"); /*更新关系嵌入值*/
                if (!pFuncUpdateEntity || !PyCallable_Check(pFuncUpdateEntity))
                {
                    printf("not found function updateEntityEmbedding");
                    return;
                }
                PyObject *pArgs = PyTuple_New(2);
                PyTuple_SetItem(pArgs, 0, Py_BuildValue("s", tmpEmbStr.c_str())); 
                PyTuple_SetItem(pArgs, 1, Py_BuildValue("i", i));

                PyObject *pRet = PyObject_CallObject(pFuncUpdateEntity, pArgs);
            }
    }

    void bfgs()
    {
        res = 0;
        int nbatches = 100;
        int nepoch = 1000;        
        int batchsize = fb_h.size() / nbatches;
        for (int epoch = 0; epoch < nepoch; epoch++)
        {

            res = 0;
            for (int batch = 0; batch < nbatches; batch++)
            {
                relation_tmp = relation_vec;
                entity_tmp = entity_vec;
                for (int k = 0; k < batchsize; k++)
                {
                    int i = rand_max(fb_h.size());
                    int j = rand_max(entity_num);
                    double pr = 1000 * right_num[fb_r[i]] / (right_num[fb_r[i]] + left_num[fb_r[i]]);
                    if (method == 0)
                        pr = 500;
                    /* random replace head entity or tail entity*/
                    if (rand() % 1000 < pr)
                    {
                        while (ok[make_pair(fb_h[i], fb_r[i])].count(j) > 0)
                            j = rand_max(entity_num);
                        train_kb(fb_h[i], fb_l[i], fb_r[i], fb_h[i], j, fb_r[i]);
                    }
                    else
                    {
                        while (ok[make_pair(j, fb_r[i])].count(fb_l[i]) > 0)
                            j = rand_max(entity_num);
                        train_kb(fb_h[i], fb_l[i], fb_r[i], j, fb_l[i], fb_r[i]);
                    }
                    norm(relation_tmp[fb_r[i]]);
                    norm(entity_tmp[fb_h[i]]);
                    norm(entity_tmp[fb_l[i]]);
                    norm(entity_tmp[j]);
                }
                relation_vec = relation_tmp;
                entity_vec = entity_tmp;
            }
            cout << "epoch:" << epoch << ' ' << res << endl;
            

            f2 = fopen(("relation2vec." + version).c_str(), "w");
            f3 = fopen(("entity2vec." + version).c_str(), "w");

            std::string suffix = ".txt";
            relationFile = fopen(("/home/lpk/data/Test/relation2vec" + suffix).c_str(), "w");
            entityFile = fopen(("/home/lpk/data/Test/entity2vec" + suffix).c_str(), "w");
            
            /* print relation_vec to relation2vec.bern file*/

            for (int i = 0; i < relation_num; i++)
            {
                fprintf(relationFile, "%s\t", id2relation[i].c_str());
                fprintf(relationFile, "%d\t", i);
                std::stringstream embeddingVal; 
                embeddingVal.precision(6);
                embeddingVal.setf(std::ios::fixed);
                embeddingVal<<"{";
                for (int ii = 0; ii < n; ii++){     //Original writing method for comparing
                    
                    fprintf(f2, "%.6lf\t", relation_vec[i][ii]);
                    if(ii == n - 1){
                        embeddingVal << relation_vec[i][ii];
                    }
                    else{
                        embeddingVal << relation_vec[i][ii] <<",";
                    }
                    
                }
                embeddingVal<<"}";
                string tmpEmbStr;
                tmpEmbStr = embeddingVal.str();
                fprintf(relationFile, "%s\n", tmpEmbStr.c_str());

                fprintf(f2, "\n");

            }
            
            // /* print entity_vec to entity2vec.bern file*/
            for (int i = 0; i < entity_num; i++)
            {
                fprintf(entityFile, "%s\t", id2entity[i].c_str());
                fprintf(entityFile, "%d\t", i);
                std::stringstream embeddingVal;
                embeddingVal.precision(6);
                embeddingVal.setf(std::ios::fixed);
                embeddingVal<<"{";
                for (int ii = 0; ii < n; ii++){
                    
                    fprintf(f3, "%.6lf\t", entity_vec[i][ii]);
                    if(ii == n - 1){
                        embeddingVal << entity_vec[i][ii];
                    }
                    else{
                        embeddingVal << entity_vec[i][ii] <<",";
                    }
                    
                }
                embeddingVal<<"}";
                string tmpEmbStr;
                tmpEmbStr = embeddingVal.str();
                fprintf(entityFile, "%s\n", tmpEmbStr.c_str());


                fprintf(f3, "\n");
            }

            fclose(f2);
            fclose(f3);
            fclose(entityFile);
            fclose(relationFile);
        }
        
        // writeDataBase();    //Single sentence execution update database embedding for comparison
        txtToDatabase();


    }
    double res1;
    double calc_sum(int e1, int e2, int rel)
    {
        double sum = 0;
        if (L1_flag)
            for (int ii = 0; ii < n; ii++)
                sum += fabs(entity_vec[e2][ii] - entity_vec[e1][ii] - relation_vec[rel][ii]);
        else
            for (int ii = 0; ii < n; ii++)
                sum += sqr(entity_vec[e2][ii] - entity_vec[e1][ii] - relation_vec[rel][ii]);
        return sum;
    }
    void gradient(int e1_a, int e2_a, int rel_a, int e1_b, int e2_b, int rel_b)
    {
        for (int ii = 0; ii < n; ii++)
        {

            double x = 2 * (entity_vec[e2_a][ii] - entity_vec[e1_a][ii] - relation_vec[rel_a][ii]);
            if (L1_flag)
                if (x > 0)
                    x = 1;
                else
                    x = -1;
            relation_tmp[rel_a][ii] -= -1 * rate * x;
            entity_tmp[e1_a][ii] -= -1 * rate * x;
            entity_tmp[e2_a][ii] += -1 * rate * x;
            x = 2 * (entity_vec[e2_b][ii] - entity_vec[e1_b][ii] - relation_vec[rel_b][ii]);
            if (L1_flag)
                if (x > 0)
                    x = 1;
                else
                    x = -1;
            relation_tmp[rel_b][ii] -= rate * x;
            entity_tmp[e1_b][ii] -= rate * x;
            entity_tmp[e2_b][ii] += rate * x;
        }
    }
    void train_kb(int e1_a, int e2_a, int rel_a, int e1_b, int e2_b, int rel_b)
    {
        double sum1 = calc_sum(e1_a, e2_a, rel_a);
        double sum2 = calc_sum(e1_b, e2_b, rel_b);
        if (sum1 + margin > sum2)
        {
            res += margin + sum1 - sum2;
            gradient(e1_a, e2_a, rel_a, e1_b, e2_b, rel_b);
        }
    }
};

Train train;


int prepare0(){
    PyObject *pFunc0 = PyObject_GetAttrString(pModule, "totalNum"); /*Calculate the number of tuples in trian2id*/
    if (!pFunc0 || !PyCallable_Check(pFunc0))
    {
        printf("not found function totalNum");
        return -1;
    }
    Py_DECREF(pFunc0);
    PyObject *pRet0 = PyObject_CallObject(pFunc0, NULL);
    int trainTotal = 0;
    PyArg_Parse(pRet0, "i", &trainTotal);
    Py_DECREF(pRet0);
    return trainTotal;    
}

void prepare1(){
    PyObject *pFunc2 = PyObject_GetAttrString(pModule, "totalEntNum"); /*Calculate the number of tuples in entity2id*/
    if (!pFunc2 || !PyCallable_Check(pFunc2))
    {
        printf("not found function totalEntNum");
        return;
    }
    PyObject *pRet2 = PyObject_CallObject(pFunc2, NULL);
    Py_DECREF(pFunc2);
    PyArg_Parse(pRet2, "i", &entity_num);
    Py_DECREF(pRet2);
    printf(" The total num for entity is %d \n", entity_num);
    PyObject *pEntListFunc = PyObject_GetAttrString(pModule, "getEntList");
    PyObject *pgetEntListRet = PyObject_CallObject(pEntListFunc, NULL);
    Py_DECREF(pEntListFunc);
    
    PyObject *ListItem;
    PyObject *Item;

    for (int i = 0; i < entity_num; i++)
    {
        ListItem = PyList_GetItem(pgetEntListRet, i);
        Item = PyList_GetItem(ListItem, 0);

        char *tmpStrc;
        string tmpStr;
        PyArg_Parse(Item, "s", &tmpStrc);
	    tmpStr=tmpStrc;

        int tmpId;
        Item = PyList_GetItem(ListItem, 1);
        PyArg_Parse(Item, "i", &tmpId);

        entity2id[tmpStr] = tmpId;
        id2entity[tmpId] = tmpStr;

    }
    Py_DECREF(pgetEntListRet);


    PyObject *pFunc3 = PyObject_GetAttrString(pModule, "totalRelNum"); /*Calculate the number of tuples in relation2id*/
    if (!pFunc3 || !PyCallable_Check(pFunc3))
    {
        printf("not found function totalRelNum");
        return;
    }
    PyObject *pRet3 = PyObject_CallObject(pFunc3, NULL);
    Py_DECREF(pFunc3);
    PyArg_Parse(pRet3, "i", &relation_num);
    Py_DECREF(pRet3);
    printf(" The total num for relNum is %d \n", relation_num);

    PyObject *pRelListFunc = PyObject_GetAttrString(pModule, "getRelList");
    PyObject *pgetRelListRet = PyObject_CallObject(pRelListFunc, NULL);
    Py_DECREF(pRelListFunc);
    // "SELECT * from relation2id"
    for (int i = 0; i < relation_num; i++)
    {
        ListItem = PyList_GetItem(pgetRelListRet, i);
        Item = PyList_GetItem(ListItem, 0);

        char *tmpStrc;
	    string tmpStr;
        PyArg_Parse(Item, "s", &tmpStrc);
	    tmpStr=tmpStrc;
        //printf("%d ",result);

        int tmpId;
        Item = PyList_GetItem(ListItem, 1);
        PyArg_Parse(Item, "i", &tmpId);
        // printf("%d ",result);

        relation2id[tmpStr] = tmpId;
        id2relation[tmpId] = tmpStr;

    }
    Py_DECREF(pgetRelListRet);
}

void prepare(int limit, int offset)
{

    PyObject *ListItem;
    PyObject *Item;

    PyObject *pFunc = PyObject_GetAttrString(pModule, "totalNumForBulk"); /*Calculate the number of tuples in trian2id*/
    if (!pFunc || !PyCallable_Check(pFunc))
    {
        printf("not found function totalNumForBulk");
        return;
    }
    Py_DECREF(pFunc);
    PyObject *pArgs = PyTuple_New(2);
    PyTuple_SetItem(pArgs, 0, Py_BuildValue("i", limit)); 
    PyTuple_SetItem(pArgs, 1, Py_BuildValue("i", offset));

    PyObject *pRet = PyObject_CallObject(pFunc, pArgs);
    int trainTotal = 0;
    PyArg_Parse(pRet, "i", &trainTotal);
    Py_DECREF(pRet);
    printf("The total num for train triple is %d , the limit is %d , the offset is %d \n", trainTotal, limit, offset);

    PyObject *pListFunc = PyObject_GetAttrString(pModule, "getListForBulk");
    PyObject *pgetListRet = PyObject_CallObject(pListFunc, pArgs);
    Py_DECREF(pArgs);

    for (int i = 0; i < trainTotal; i++)
    {
        ListItem = PyList_GetItem(pgetListRet, i);
        Item = PyList_GetItem(ListItem, 0);
        char *tmpS1c;
        string tmpS1;
        PyArg_Parse(Item, "s", &tmpS1c);
	    tmpS1=tmpS1c;
        Item = PyList_GetItem(ListItem, 1);
        char *tmpS2c;
        string tmpS2;
        PyArg_Parse(Item, "s", &tmpS2c);
	    tmpS2=tmpS2c;

        Item = PyList_GetItem(ListItem, 2);
        char *tmpS3c;
        PyArg_Parse(Item, "s", &tmpS3c);
        string tmpS3;
	    tmpS3=tmpS3c;

        if (entity2id.count(tmpS1) == 0)
        {
            cout << "miss111 entity:" << tmpS1 << endl;
        }
        if (entity2id.count(tmpS2) == 0)
        {
            cout << "miss222 entity:" << tmpS2 << endl;
        }
        if (relation2id.count(tmpS3) == 0)
        {
            relation2id[tmpS3] = relation_num;
            relation_num++;
        }
        left_entity[relation2id[tmpS3]][entity2id[tmpS1]]++;
        right_entity[relation2id[tmpS3]][entity2id[tmpS2]]++;
        train.add(entity2id[tmpS1], entity2id[tmpS2], relation2id[tmpS3]);
    }
 
    
    for (int i = 0; i < relation_num; i++)
    {
        double sum1 = 0, sum2 = 0;
        for (map<int, int>::iterator it = left_entity[i].begin(); it != left_entity[i].end(); it++)
        {
            sum1++;
            sum2 += it->second;
        }
        left_num[i] = sum2 / sum1;
    }
    for (int i = 0; i < relation_num; i++)
    {
        double sum1 = 0, sum2 = 0;
        for (map<int, int>::iterator it = right_entity[i].begin(); it != right_entity[i].end(); it++)
        {
            sum1++;
            sum2 += it->second;
        }
        right_num[i] = sum2 / sum1;
    }
    cout << "relation_num=" << relation_num << endl;
    cout << "entity_num=" << entity_num << endl;
}

int ArgPos(char *str, int argc, char **argv)
{
    int a;
    for (a = 1; a < argc; a++)
        if (!strcmp(str, argv[a]))
        {
            if (a == argc - 1)
            {
                printf("Argument missing for %s\n", str);
                exit(1);
            }
            return a;
        }
    return -1;
}

int main(int argc, char **argv)
{
    Py_Initialize(); 
    PyRun_SimpleString("import psycopg2");
    PyRun_SimpleString("import agensgraph");
    PyRun_SimpleString("import sys");
    PyRun_SimpleString("sys.path.append('/home/lpk/code/script')");

    pModule = PyImport_ImportModule("Py2Cpp");
    if (pModule == NULL)
    {
        printf("module not found");
        return 0;
    }
    
    srand((unsigned)time(NULL));
    int method = 1;
    int n = 100;
    double rate = 0.001;
    double margin = 1;
    int i;
    if ((i = ArgPos((char *)"-size", argc, argv)) > 0)
        n = atoi(argv[i + 1]);
    if ((i = ArgPos((char *)"-margin", argc, argv)) > 0)
        margin = atoi(argv[i + 1]);
    if ((i = ArgPos((char *)"-method", argc, argv)) > 0)
        method = atoi(argv[i + 1]);

    cout<<"-------------------------Training data information------------------------"<<endl;    
    cout << " size = " << n << endl;
    cout << " learing rate = " << rate << endl;
    cout << " margin = " << margin << endl;
    if (method)
        version = "bern";
    else
        version = "unif";
    cout << " method = " << version << endl;

    int numForTrain = prepare0();

    prepare1();
    int bulksize = 1600000000;
    //Training in batches, size of each training
    int bulktimes = numForTrain/bulksize;
    int bulklast = numForTrain%bulksize;
    int limit = bulksize;
    int offset = 0;

    cout<<"-------------------------Batch training information------------------------\n The num fo trian is "<<numForTrain << "\n The number of bulksize is " <<bulksize <<"\n The number of bulktimes is "<< bulktimes + 1 << "\n ";

    train.initialize(n, rate, margin, method);

    initializeFlag = 0;

    for(int i = 0; i < bulktimes; i++){
        cout<<"Bulk "<<i<<endl;
        prepare(limit, offset);
        train.run(n, rate, margin, method);
        offset = offset + bulksize;
    }
    cout<<"Bulk  "<<bulktimes + 1<<endl;
    prepare(bulklast, offset);
    train.run(n, rate, margin, method);

    PyObject *pFuncClose = PyObject_GetAttrString(pModule, "closeConnect");
        if (!pFuncClose || !PyCallable_Check(pFuncClose))
        {
            printf("not found function closeConnect");
            return 0;
        }

    PyObject *pRetClose = PyObject_CallObject(pFuncClose, NULL);
    Py_Finalize();
}
